#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time      :2021/7/16 14:33
# @Author    :cjw
from typing import List, Optional, Generic, TypeVar, Type

from fastapi.encoders import jsonable_encoder
from fastapi import HTTPException, status
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session

from app.db.session import Base

# 定义类型变量
ModelType = TypeVar('ModelType', bound=Base)
CreateSchemaType = TypeVar('CreateSchemaType', bound=BaseModel)
UpdateSchemaType = TypeVar('UpdateSchemaType', bound=BaseModel)


class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
    """
    增删改查基类, 继承类型变量，定义的方法返回继承的类型变量
    """

    def __init__(self, model: Type[ModelType]):
        """
        用默认方法创建、读取、更新、删除（CRUD）的CRUD对象

        **参数**

        * `model`: SQLAlchemy模型类
        * `schema`: Pydantic模型（schema）类
        """
        self.model = model

    def get(self, db: Session, m_id) -> Optional[ModelType]:
        """
        根据id查询一条数据
        :param db: 数据库连接
        :param m_id: 要获取表记录数据的id主键
        :return:
        """
        return db.get(self.model, m_id)

    def get_multi(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[ModelType]:
        """
        获取多条数据
        :param db: 数据库连接
        :param skip: 查询截取的起始位置
        :param limit: 查询多少条数据
        :return:
        """
        stmt = select(self.model).offset(skip).limit(limit)
        return [st for st in db.execute(stmt).scalars()]

    def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
        """
        新增数据库数据
        :param db: 数据库连接
        :param obj_in: 新增数据schema类型
        :return:
        """
        # schema对象数据转化为字典，然后赋值给数据库模型对象
        obj_in_data = jsonable_encoder(obj_in)
        db_obj = self.model(**obj_in_data)

        # 保存数据并提交
        db.add(db_obj)
        db.commit()
        return db_obj

    def update(self, db: Session, *, db_obj: ModelType,
               obj_in: UpdateSchemaType) -> ModelType:
        """
        更新数据
        :param db: 数据库连接
        :param db_obj: 数据库模型对象
        :param obj_in: schema更新类型
        :return:
        """
        # 数据库模型对象转化为字典，schema对象剔除默认值属性转化为字典
        obj_data = jsonable_encoder(db_obj)
        update_data = obj_in.dict(exclude_unset=True)

        # 遍历数据库模型对象的属性，如果更新对象存在一致的属性，就把对象的值赋值过去
        for field in obj_data:
            if field in update_data:
                setattr(db_obj, field, update_data.get(field))

        db.add(db_obj)
        db.commit()
        return db_obj

    def delete(self, db: Session, *, m_id) -> ModelType:
        """
        删除数据
        :param db: 数据库连接
        :param m_id: 要删除表数据记录的id主键
        :return:
        """
        obj = db.get(self.model, m_id)
        if not obj:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail='删除的用户不存在！'
            )
        db.delete(obj)
        db.commit()
        return obj
